package com.shakespace.artofandroid.chapter02.binderpool

import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import android.util.Log
import com.shakespace.artofandroid.IBinderPool
import com.shakespace.firstlinecode.global.TAG
import java.util.concurrent.CountDownLatch

class BinderPool(var context: Context) {

    private var binderPool: IBinderPool? = null

    lateinit var countDownLatch: CountDownLatch

    private val binderPoolConnection = object : ServiceConnection {
        override fun onServiceDisconnected(name: ComponentName?) {
            // ignore
        }

        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            // get binderPool
            binderPool = IBinderPool.Stub.asInterface(service)
            try {
                binderPool?.asBinder()?.linkToDeath(deathRecipient, 0)
            } catch (e: Exception) {
                Log.e(this.TAG, "onServiceConnected: ${e.localizedMessage}")
            }
            countDownLatch.countDown()
        }
    }

    private val deathRecipient = object : IBinder.DeathRecipient {
        override fun binderDied() {
            binderPool?.asBinder()?.unlinkToDeath(this, 0)
            binderPool = null
            connectBinderPoolService()
        }
    }

    /**
     * // 2020-01-14 NOTE:  if you have some Field will be used in init(),you must put then before  init() , or will get NPE
     * e.g
     *  init{ a + b}
     *  val a = 1
     *  val b = 2
     *      -- > java code
     *      Class (){
     *          a + b
     *          int a = 1
     *          int b = 2
     *      }
     */
    init {
        // bindService when init
        connectBinderPoolService()
    }

//    constructor(a:Int,context: Context):this(context){
//    }

    fun queryBinder(binderCode: Int): IBinder? {
        // get Particular Binder by binderCode
        binderPool?.apply {
            return binderPool?.queryBinder(binderCode)
        }
        return null
    }


    // 2020-01-14 NOTE: kotlin donot have keyword synchronized , use annotaion instead
    /**
     * CountDownLatch(int count) //实例化一个倒计数器，count指定计数个数
     *  countDown() // 计数减一
     * await() //等待，当计数减到0时，所有线程并行执行
     *
     * //调用await()方法的线程会被挂起，它会等待直到count值为0才继续执行
     *  public void await() throws InterruptedException { };
     *  和await()类似，只不过等待一定的时间后count值还没变为0的话就会继续执行
     *  public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };
     *  将count值减1
     *  public void countDown() { };
     *
     */
    @Synchronized
    private fun connectBinderPoolService() {
        countDownLatch = CountDownLatch(1)
        val intent = Intent(context, BinderPoolService::class.java)
        context.bindService(intent, binderPoolConnection, Context.BIND_AUTO_CREATE)
        try {
            countDownLatch.await()  // so , it will go on only when onServiceConnected
        } catch (e: Exception) {
            Log.e(this.TAG, "connectBinderPoolService: ${e.localizedMessage}")
        }
    }

    companion object {
        @SuppressLint("StaticFieldLeak")
        var instance: BinderPool? = null

        fun getInstance(context: Context): BinderPool {
            if (instance == null) {
                instance = BinderPool(context.applicationContext)
            }
            return instance!!
        }
    }
}